﻿using SimpleModbus;
using System;
using System.Collections.Generic;
using System.Net;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows.Forms;

namespace ModbusTCPExp
{
    public partial class FrmMain : Form
    {

        //实例化
        ModbusTCP objModbusTCP = new ModbusTCP();

        #region 属性

        /// <summary>
        /// 自动获取时间的属性
        /// </summary>
        public string CurrentTime => DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss");

        /// <summary>
        /// 网络是否连接
        /// </summary>
        private bool _isConnected = false;
        public bool IsConnected
        {
            get { return _isConnected; }
            set { _isConnected = value; }
        }

        #endregion


        /// <summary>
        /// 构造函数
        /// </summary>
        public FrmMain()
        {
            InitializeComponent();

            this.Load += FrmMain_Load;
        }


        /// <summary>
        /// 主界面加载时初始化部分信息
        /// </summary>
        private void FrmMain_Load(object sender, EventArgs e)
        {
            //初始化下拉菜单
            this.cmb_dataFormat.DataSource = Enum.GetNames(typeof(DataFormat));//数据格式
            this.cmb_storeArea.DataSource = Enum.GetNames(typeof(StoreArea));//存储区
            this.cmb_varType.DataSource = Enum.GetNames(typeof(VarType));//变量类型


            //初始化listView
            this.lisview_info.Columns[1].Width = this.lisview_info.ClientSize.Width - this.lisview_info.Columns[0].Width;//调整第二列可显示长度
        }

        /// <summary>
        /// 建立连接
        /// </summary>
        private async void btn_connect_Click(object sender, EventArgs e)
        {
            IPAddress ip = null;
            int port;

            if (IsConnected)
            {
                return;
            }

            //输入合法性检查
            if (!IPAddress.TryParse(this.txt_ipAddress.Text.Trim(), out ip))
            {
                AddLog(1, "IP地址格式不合法！");
                return;
            }

            if (!int.TryParse(this.txt_port.Text.Trim(), out port))
            {
                AddLog(1, "端口号格式不合法！");
                return;
            }


            //调用连接
            try
            {
                await objModbusTCP?.ConnectAsync(ip.ToString(), port.ToString());
                IsConnected = true;
                AddLog(0, "连接成功！");
            }
            catch (Exception ex)
            {

                AddLog(2, "连接失败！" + ex.Message);
            }

        }

        /// <summary>
        /// 断开连接
        /// </summary>
        private void btn_disConnect_Click(object sender, EventArgs e)
        {
            if (!IsConnected)
                return;

            try
            {
                objModbusTCP?.Disconnect();
            }
            catch (Exception ex)
            {
                AddLog(2, "断开连接失败！" + ex.Message);
                return;
            }

            IsConnected = false;
            AddLog(0, "断开连接成功！");

        }

        /// <summary>
        /// 添加记录信息
        /// </summary>
        private void AddLog(int type, string info)
        {
            //新创建一条记录
            ListViewItem item = new ListViewItem(" " + CurrentTime, type);//主文本（第一列）
            item.SubItems.Add(info);//子项（二列及其以后）
            /*
            ...item.SubItems.Add(3)
            ...item.SubItems.Add(4)
            ...item.SubItems.Add(5)
            ...item.SubItems.Add(6)
            ...假如有更多子项
            */
            this.lisview_info.Items.Insert(0, item);
        }

        /// <summary>
        /// 读取
        /// </summary>
        private async void btn_read_Click(object sender, EventArgs e)
        {
            //校验数据合法性
            var validResult = ValidateInputs();

            if (validResult.isValid) return;

            //执行读取
            var storeArea = Enum.Parse(typeof(StoreArea), this.cmb_storeArea.SelectedItem.ToString());//存储区
            var varType = Enum.Parse(typeof(VarType), this.cmb_varType.SelectedItem.ToString());//数据类型

            byte[] result = null;
            switch (varType)
            {
                //Bit类型
                case VarType.Bit:
                    switch (storeArea)
                    {
                        case StoreArea.输出线圈0x:
                            result = await objModbusTCP.ReadOutputStatusAsync(validResult.address, validResult.count, validResult.slaveAddress);
                            break;
                        case StoreArea.输入线圈1x:
                            result = await objModbusTCP.ReadInputStatusAsync(validResult.address, validResult.count, validResult.slaveAddress);
                            break;
                        case StoreArea.输入寄存器3x:
                        case StoreArea.保持寄存器4x:
                            return;
                        default:
                            break;
                    }

                    //数据的显示
                    if (result != null)
                    {
                        string binaryString = string.Empty;
                        foreach (var item in result)
                        {
                            char[] c = Convert.ToString(item, 2).PadLeft(8, '0').ToCharArray();
                            Array.Reverse(c);//翻转
                            binaryString += new string(c);//将字符串数组转换成字符串
                        }

                        //截取
                        AddLog(0, $"读取成功，结果为：{binaryString.Substring(0, validResult.count)}");
                        return;
                    }
                    else
                    {
                        AddLog(0, $"读取失败");
                    }

                    break;

                //ushort类型
                case VarType.UShort:
                    switch (storeArea)
                    {
                        case StoreArea.保持寄存器4x:
                            result = await objModbusTCP.ReadHoldRegistersAsync(validResult.address, validResult.count, validResult.slaveAddress);
                            break;
                        case StoreArea.输入寄存器3x:
                            result = await objModbusTCP.ReadInputRegistersAsync(validResult.address, validResult.count, validResult.slaveAddress);
                            break;
                        case StoreArea.输出线圈0x:
                        case StoreArea.输入线圈1x:
                            return;
                        default:
                            break;
                    }

                    //数据显示
                    if (result != null && result.Length == validResult.count * 2)
                    {
                        string shortString = string.Empty;
                        for (int i = 0; i < result.Length; i += 2)
                        {
                            byte[] b1 = ModbusFormatTool.Get16ByteArray(result, i, objModbusTCP.DataFormat);
                            shortString += BitConverter.ToUInt16(b1, 0).ToString() + " ";
                        }

                        AddLog(0, $"读取成功，结果为：{shortString.Trim()}");
                    }

                    else
                    {
                        AddLog(1, $"读取失败");
                    }

                    break;

                //short类型
                case VarType.Short:
                    switch (storeArea)
                    {
                        case StoreArea.保持寄存器4x:
                            result = await objModbusTCP.ReadHoldRegistersAsync(validResult.address, validResult.count, validResult.slaveAddress);
                            break;
                        case StoreArea.输入寄存器3x:
                            result = await objModbusTCP.ReadInputRegistersAsync(validResult.address, validResult.count, validResult.slaveAddress);
                            break;
                        case StoreArea.输出线圈0x:
                        case StoreArea.输入线圈1x:
                            return;
                        default:
                            break;
                    }

                    //数据显示
                    if (result != null && result.Length == validResult.count * 2)
                    {
                        string shortString = string.Empty;
                        for (int i = 0; i < result.Length; i += 2)
                        {
                            byte[] b1 = ModbusFormatTool.Get16ByteArray(result, i, objModbusTCP.DataFormat);
                            shortString += BitConverter.ToInt16(b1, 0).ToString() + " ";
                        }

                        AddLog(0, $"读取成功，结果为：{shortString.Trim()}");
                    }

                    else
                    {
                        AddLog(1, $"读取失败");
                    }

                    break;


                //int类型
                case VarType.Int:
                    switch (storeArea)
                    {
                        case StoreArea.保持寄存器4x:
                            result = await objModbusTCP.ReadHoldRegistersAsync(validResult.address, validResult.count * 2, validResult.slaveAddress);
                            break;
                        case StoreArea.输入寄存器3x:
                            result = await objModbusTCP.ReadInputRegistersAsync(validResult.address, validResult.count * 2, validResult.slaveAddress);
                            break;
                        case StoreArea.输出线圈0x:
                        case StoreArea.输入线圈1x:
                            return;
                        default:
                            break;
                    }

                    //数据显示
                    if (result != null && result.Length == validResult.count * 4)
                    {
                        string shortString = string.Empty;
                        for (int i = 0; i < result.Length; i += 4)
                        {
                            byte[] b1 = ModbusFormatTool.Get32ByteArray(result, i, objModbusTCP.DataFormat);
                            shortString += BitConverter.ToInt32(b1, 0).ToString() + " ";
                        }

                        AddLog(0, $"读取成功，结果为：{shortString.Trim()}");
                    }

                    else
                    {
                        AddLog(1, $"读取失败");
                    }
                    break;

                //uint类型
                case VarType.UInt:
                    switch (storeArea)
                    {
                        case StoreArea.保持寄存器4x:
                            result = await objModbusTCP.ReadHoldRegistersAsync(validResult.address, validResult.count * 2, validResult.slaveAddress);
                            break;
                        case StoreArea.输入寄存器3x:
                            result = await objModbusTCP.ReadInputRegistersAsync(validResult.address, validResult.count * 2, validResult.slaveAddress);
                            break;
                        case StoreArea.输出线圈0x:
                        case StoreArea.输入线圈1x:
                            return;
                        default:
                            break;
                    }

                    //数据显示
                    if (result != null && result.Length == validResult.count * 4)
                    {
                        string shortString = string.Empty;
                        for (int i = 0; i < result.Length; i += 4)
                        {
                            byte[] b1 = ModbusFormatTool.Get32ByteArray(result, i, objModbusTCP.DataFormat);
                            shortString += BitConverter.ToUInt32(b1, 0).ToString() + " ";
                        }

                        AddLog(0, $"读取成功，结果为：{shortString.Trim()}");
                    }

                    else
                    {
                        AddLog(1, $"读取失败");
                    }
                    break;

                //float类型
                case VarType.Float:
                    switch (storeArea)
                    {
                        case StoreArea.保持寄存器4x:
                            result = await objModbusTCP.ReadHoldRegistersAsync(validResult.address, validResult.count * 2, validResult.slaveAddress);
                            break;
                        case StoreArea.输入寄存器3x:
                            result = await objModbusTCP.ReadInputRegistersAsync(validResult.address, validResult.count * 2, validResult.slaveAddress);
                            break;
                        case StoreArea.输出线圈0x:
                        case StoreArea.输入线圈1x:
                            return;
                        default:
                            break;
                    }

                    //数据显示
                    if (result != null && result.Length == validResult.count * 4)
                    {
                        string shortString = string.Empty;
                        for (int i = 0; i < result.Length; i += 4)
                        {
                            byte[] b1 = ModbusFormatTool.Get32ByteArray(result, i, objModbusTCP.DataFormat);
                            shortString += BitConverter.ToSingle(b1, 0).ToString() + " ";
                        }

                        AddLog(0, $"读取成功，结果为：{shortString.Trim()}");
                    }

                    else
                    {
                        AddLog(1, $"读取失败");
                    }
                    break;
                default:
                    break;
            }

        }

        /// <summary>
        /// 写入
        /// </summary>
        private async void btn_write_Click(object sender, EventArgs e)
        {
            //校验数据合法性
            var validResult = ValidateInputs();

            if (!validResult.isValid)
            {
                AddLog(1, "输入验证失败");
                return;
            }

            //报文文本信息
            string setText = this.txt_setValue.Text.Trim();

            //执行写入
            var storeArea = (StoreArea)Enum.Parse(typeof(StoreArea), this.cmb_storeArea.SelectedItem.ToString());//存储区
            var varType = (VarType)Enum.Parse(typeof(VarType), this.cmb_varType.SelectedItem.ToString());//数据类型

            bool result = false;//结果

            switch (varType)
            {
                case VarType.Bit:
                    switch (storeArea)
                    {
                        case StoreArea.输出线圈0x:

                            //发送报文
                            result = await objModbusTCP.ForceMultiCoilAsync(validResult.address, StringToBoolArray(setText), validResult.slaveAddress);
                            break;
                        case StoreArea.输入线圈1x:
                        case StoreArea.输入寄存器3x:
                        case StoreArea.保持寄存器4x:
                            AddLog(1, "写入存储区域不合法");
                            return;
                    }

                    AddLog(result ? 0 : 1, result ? $"写入成功,写入数值{setText}" : "写入失败");

                    break;
                case VarType.Short:
                    switch (storeArea)
                    {
                        case StoreArea.保持寄存器4x:
                            short sendData = 0;
                            if (short.TryParse(setText, out sendData))
                            {
                                result = await objModbusTCP.ForceSingleRegisterAsync(validResult.address, sendData, validResult.slaveAddress);
                            }
                            else
                            {
                                AddLog(1, "写入失败,数据格式不合法");
                            }
                            break;
                        case StoreArea.输入寄存器3x:
                        case StoreArea.输出线圈0x:
                        case StoreArea.输入线圈1x:
                            AddLog(1, "写入存储区域不合法");
                            return;
                    }

                    AddLog(result ? 0 : 1, result ? $"写入成功,写入数值{setText}" : "写入失败");

                    break;
                case VarType.UShort:
                    switch (storeArea)
                    {
                        case StoreArea.保持寄存器4x:
                            ushort[] sendData = GetSendDataArray<ushort>(setText, VarType.UShort);
                            result = objModbusTCP.ForceMultiRegister(validResult.address, sendData, validResult.slaveAddress);
                            break;

                        case StoreArea.输入寄存器3x:
                        case StoreArea.输出线圈0x:
                        case StoreArea.输入线圈1x:
                            AddLog(1, "写入存储区域不合法");
                            return;
                    }

                    AddLog(result ? 0 : 1, result ? $"写入成功,写入数值{setText}" : "写入失败");
                    break;
                case VarType.Int:
                    switch (storeArea)
                    {
                        case StoreArea.保持寄存器4x:
                            int[] sendData = GetSendDataArray<int>(setText, VarType.Int);
                            result = objModbusTCP.ForceMultiRegister(validResult.address, sendData, validResult.slaveAddress);
                            break;
                        case StoreArea.输入寄存器3x:
                        case StoreArea.输出线圈0x:
                        case StoreArea.输入线圈1x:
                            AddLog(1, "写入存储区域不合法");
                            return;
                    }

                    AddLog(result ? 0 : 1, result ? $"写入成功,写入数值{setText}" : "写入失败");
                    break;
                case VarType.UInt:
                    switch (storeArea)
                    {
                        case StoreArea.保持寄存器4x:
                            uint[] sendData = GetSendDataArray<uint>(setText, VarType.UInt);
                            result = objModbusTCP.ForceMultiRegister(validResult.address, sendData, validResult.slaveAddress);
                            break;
                        case StoreArea.输入寄存器3x:
                        case StoreArea.输出线圈0x:
                        case StoreArea.输入线圈1x:
                            AddLog(1, "写入存储区域不合法");
                            return;
                    }

                    AddLog(result ? 0 : 1, result ? $"写入成功,写入数值{setText}" : "写入失败");
                    break;
                case VarType.Float:
                    switch (storeArea)
                    {
                        case StoreArea.保持寄存器4x:
                            float[] sendData = GetSendDataArray<float>(setText, VarType.Float);
                            result = objModbusTCP.ForceMultiRegister(validResult.address, sendData, validResult.slaveAddress);
                            break;
                        case StoreArea.输入寄存器3x:
                        case StoreArea.输出线圈0x:
                        case StoreArea.输入线圈1x:
                            AddLog(1, "写入存储区域不合法");
                            return;
                    }

                    AddLog(result ? 0 : 1, result ? $"写入成功,写入数值{setText}" : "写入失败");
                    break;
                default:
                    break;
            }
        }

        #region 工具方法

        /// <summary>
        /// 数据格式变化
        /// </summary>
        private void cmb_dataFormat_SelectedIndexChanged(object sender, EventArgs e)
        {
            if (objModbusTCP != null)
            {
                objModbusTCP.DataFormat = (DataFormat)Enum.Parse(typeof(DataFormat), this.cmb_dataFormat.SelectedItem.ToString());
            }
        }

        /// <summary>
        /// 字符串转换为bool数组    
        /// </summary>
        private bool[] StringToBoolArray(string val)
        {
            List<bool> result = new List<bool>();

            //判断是否包含空格，即是强制单线圈还是强制多线圈
            if (val.Contains(" "))
            {
                string[] str = Regex.Split(val, "\\s+", RegexOptions.IgnoreCase);//"\\s+"表示一个或多个空格

                foreach (var item in str)
                {
                    result.Add(item.ToLower() == "true" || item == "1");
                }
            }
            else
            {
                result.Add(val.ToLower() == "true" || val == "1");
            }

            return result.ToArray();
        }

        /// <summary>
        /// 校验面板数据输入合法性
        /// </summary>
        /// <returns></returns>
        private (bool isValid, ushort address, ushort count, int slaveAddress) ValidateInputs()
        {
            if (!IsConnected)
            {
                AddLog(1, "请检查通信连接状态");
                return (false, 0, 0, 0);
            }

            //数据合法性校验
            if (!ushort.TryParse(this.txt_startAddress.Text.Trim(), out ushort address))
            {
                AddLog(1, "起始地址数据格式不合法");
                return (false, 0, 0, 0);
            }
            if (!ushort.TryParse(this.txt_length.Text.Trim(), out ushort count))
            {
                AddLog(1, "长度数据格式不合法");
                return (false, 0, 0, 0);
            }
            if (!int.TryParse(this.txt_slaveAddress.Text.Trim(), out int slaveAddress))
            {
                AddLog(1, "从站地址数据格式不合法");
                return (false, 0, 0, 0);
            }

            return (true, address, count, slaveAddress);
        }

        /// <summary>
        /// 将面板数据转换为数组
        /// </summary>
        /// <param name="setValue">字符串数据</param>
        /// <param name="type">待转换类型</param>
        /// <returns>转换后数据</returns>
        private T[] GetSendDataArray<T>(string setValue, VarType type)
        {
            string[] str = Regex.Split(setValue.Trim(), "\\s+");

            T[] arr = new T[str.Length];

            for (int i = 0; i < str.Length; i++)
            {
                switch (type)
                {
                    case VarType.Bit:
                        break;
                    case VarType.Short:
                        arr[i] = (T)(object)short.Parse(str[i]);
                        break;
                    case VarType.UShort:
                        arr[i] = (T)(object)ushort.Parse(str[i]);
                        break;
                    case VarType.Int:
                        arr[i] = (T)(object)int.Parse(str[i]);
                        break;
                    case VarType.UInt:
                        arr[i] = (T)(object)uint.Parse(str[i]);
                        break;
                    case VarType.Float:
                        arr[i] = (T)(object)float.Parse(str[i]);
                        break;
                    default:
                        break;
                }
            }

            return arr;
        }

        #endregion
    }
}
